import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.Stack;
import java.util.Vector;

/**
 * Created by Administrator on 2016/6/7 0007.
 */
public class MazeAlgorithm {
    /*************** 外部接口数据 ***************/
    private Maze maze;
    private MazeLog mazeLog;
    private MazePoint mazeStartPoint;
    private MazePoint mazeEndPoint;
    private volatile MazePoint[][] mazePoints;

    /*************** 内部数据 ***************/
    private double newThreadGeneratRate;
    private int routeMaxStep;
    private int routeRate;
    private int mazeWidth;
    private int mazeHight;
    private volatile int mazeNoteNum;
    private volatile Vector<MazeDirection> currentThreadDirection;
    private boolean mazeStartFlg;       //标示迷宫当前的状态

    public MazeAlgorithm(Maze maze) throws IOException {
        mazeStartPoint  = new MazePoint();
        mazeEndPoint    = new MazePoint();
        currentThreadDirection = new Vector<MazeDirection>();
        this.maze = maze;
        mazeNoteNum = 0;
        mazeStartFlg = false;
        mazeLog = new MazeLog("./Log", "CreatMaze.log.txt");
        mazeLog.regLogString("\r\n###########################################################################");
        mazeLog.regLogString("                                  迷宫开始");
        mazeLog.regLogString("\r\n###########################################################################\r\n");
    }
    public MazePoint[][] getMazePoints(){
        return this.mazePoints;
    }
    public MazePoint getMazePoint(MazePoint mazePoint){
        return this.mazePoints[mazePoint.getMazeX()][mazePoint.getMazeY()];
    }
    public int getRouteRate(){
        return this.routeRate;
    }
    public void mazeStop(){
        this.mazeStartFlg = false;
    }
    public int getMazeWidth(){
        return this.mazeWidth;
    }
    public int getMazeHight(){
        return this.mazeHight;
    }
    public void createMaze(int width, int high){
        this.mazeWidth = width;
        this.mazeHight = high;
        mazePoints = new MazePoint[width][high];
        for(int iwdx = 0;iwdx < width;iwdx++){
            for(int ihdx = 0;ihdx < high;ihdx++){
                mazePoints[iwdx][ihdx] = new MazePoint(iwdx, ihdx);
            }
        }
        mazeNoteNum = 1;
        setMazeStartPoint();
        setMazeEndPoint();
    }
    /* 设置迷宫起点 */
    public void setMazeStartPoint(){
        Random random = new Random();
        int y = (int)random.nextInt(mazeHight);
        mazeStartPoint = mazePoints[0][y];
        mazeStartPoint.setLeftSteped(true);
        mazeStartPoint.setSteped(true);
    }
    /* 设置迷宫终点 */
    public void setMazeEndPoint(){
        Random random = new Random();
        int x,y;
        x = mazeWidth;
        y = (int)random.nextInt(mazeHight);
        mazeEndPoint.setMazeX(x);
        mazeEndPoint.setMazeY(y);
        mazeEndPoint.setLeftSteped(true);
    }
    public MazePoint getMazeStartPoint(){
        return this.mazeStartPoint;
    }
    public MazePoint getMazeEndPoint(){
        return this.mazeEndPoint;
    }
    /* 数据清理 */
    public void dataClear(){
        mazePointsInit();
    }
    /* 迷宫启动 */
    public void mazeStart(){
        mazeStartFlg = true;
        Thread thread = new Thread(new MazePathThread(getMazeStartPoint()));
        thread.start();
    }
    /*迷宫数据初始化*/
    public void mazePointsInit(){
        int widIdx = 0, higIdx = 0;
        for(widIdx = 0;widIdx < mazeWidth; widIdx++){
            for(higIdx = 0; higIdx < mazeHight; higIdx++){
                mazePoints[widIdx][higIdx].clear();
            }
        }
        try {
            mazeLog.regLogString("\r\n"+"\r\n");
            mazeLog.regLogString("==============================================================");
            mazeLog.regLogString("                            迷宫新建");
            mazeLog.regLogString("==============================================================");
            mazeLog.regLogString("\r\n"+"\r\n");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /* 迷宫中的还原迷宫中的数据 */
    public void restoreMazeFace(){
        int widIdx = 0, higIdx = 0;
        MazePoint mazePoint;
        for(widIdx = 0;widIdx < mazeWidth; widIdx++){
            for(higIdx = 0; higIdx < mazeHight; higIdx++){
                mazePoint = mazePoints[widIdx][higIdx];
                if(!mazePoint.equalsPoint(mazeStartPoint) && !mazePoint.equalsPoint(mazeStartPoint)){
                    mazePoint.clear();
                }
                else if(mazePoint.equalsPoint(mazeStartPoint)){
                    mazePoint.setUpSteped(false);
                }
            }
        }
        mazeNoteNum = 1;
    }
    public void setAlgorighmPara(MazeJFrame mazeJFrame){
        String threadRate = (String)mazeJFrame.getJCBNewThreadRate().getSelectedItem();
        String maxStep = mazeJFrame.getJTMaxStep().getText();
        String routeRate = mazeJFrame.getJTGeneratPathRate().getText();
        if(maxStep.isEmpty()){
            maxStep = "3";
        }
        if(threadRate.isEmpty()){
            threadRate = "0.2";
        }
        if(routeRate.isEmpty()){
            routeRate = "100";
        }
        setAlgorighmPara(Double.parseDouble(threadRate), Integer.parseInt(maxStep), Integer.parseInt(routeRate));
    }
    /* 设置算法参数 */
    public void setAlgorighmPara(double newThreadGeneratRate, int routeMaxStep, int routeRate){
        this.newThreadGeneratRate = newThreadGeneratRate;
        this.routeMaxStep = routeMaxStep;
        this.routeRate = routeRate;
    }
    /* 设置下一步可走 */
    public synchronized void setNextPointCanGo(MazePoint currentPoint, MazeDirection nextDirection){
        refreshMazeFaceColorSet(currentPoint, nextDirection);
        currentThreadDirection.clear();
        mazeNoteNum ++;
    }
    /* 刷新下一步界面的颜色设置和中间墙壁的颜色设置 */
    public synchronized void refreshMazeFaceColorSet(MazePoint currentPoint, MazeDirection nextDirection){
        MazePoint threadNextPoint = getNextPoint(currentPoint, nextDirection);
        switch(nextDirection){
            case UP:
                currentPoint.setUpSteped(true);
                break;
            case DOWN:
                threadNextPoint.setUpSteped(true);
                break;
            case LEFT:
                currentPoint.setLeftSteped(true);
                break;
            case RIGHT:
                threadNextPoint.setLeftSteped(true);
                break;
            default:break;
        }
        threadNextPoint.setSteped(true);
    }
    /* 函数功能：走下一步 */
    public synchronized boolean goNext(MazePathThread mazePathThread) throws IOException {
        MazeDirection nextDirection;
        MazePoint nextPoint;
        //MazePoint currentPoint = mazePathThread.getCurrentPoint();
        MazeDirection currentDirection = mazePathThread.getCurrentDirection();
        MazePoint currentNextPoint = new MazePoint();
        int currentMaxStep = mazePathThread.getCurrentMaxStepNum();
        int nextMaxtStep;

        mazeLog.regLogString("线程"+Thread.currentThread().getId()+"********************  start  ********************\r\n");
        mazeLog.regLogPrintMazeMap(mazePoints, mazeWidth, mazeHight, mazeStartPoint);
        /* 确定当前的点 *//*
        if(!haveWay(mazePathThread)){
            mazePathThread.clear();
            return false;
        }*/
        mazeLog.regLogMazePoint("线程"+Thread.currentThread().getId()+"当前的", mazePathThread.getCurrentPoint());
        //mazeLog.regLogMaxStep("线程"+Thread.currentThread().getId()+"当前的", currentMaxStep);
        /* 当前方向还没走完 */
        if(mazePathThread.getCurrentStepCount() < currentMaxStep){
            /* 确定当前方向下一步是否可走 */
            if((currentNextPoint = getNextPoint(mazePathThread.getCurrentPoint(), currentDirection)) != null){
                mazeLog.regLogStringAddTime("线程"+Thread.currentThread().getId()+"按照以前既定的方向("+currentDirection.toString()+")走的");
                mazeLog.regLogStringAddTime("线程"+Thread.currentThread().getId()+"当前已经走的步数为： "+mazePathThread.getCurrentStepCount()+"还有的步"+(currentMaxStep-mazePathThread.getCurrentStepCount()));
                /* 算法数据更新 */
                generateNewThreadHandle(mazePathThread);
                setNextPointCanGo(mazePathThread.getCurrentPoint(), currentDirection);
                mazePathThread.goOldRouteNext(getMazePoint(currentNextPoint));
                mazeLog.regLogString("线程"+Thread.currentThread().getId()+"*******************  end  ********************\r\n");
                return true;
            }
            //多线程下前方的点被其他线程给调用了,清理当前线程数据
            mazePathThread.clearCurrentStepData();
        }
        //mazeLog.regLogMazePoint("当前的5", mazePathThread.getCurrentPoint());
        nextDirection = getNextDirection();
        if(nextDirection == MazeDirection.BUTT){
            mazePathThread.clearCurrentStepData();
            return false;
        }
        mazeLog.regLogMazeDirection("线程"+Thread.currentThread().getId()+"获取的下一个的", nextDirection);
        /* 确定当前前进的最大步数 */
        nextMaxtStep = getNextMaxStep(mazePathThread, nextDirection);
        if(nextMaxtStep <= 0){
            mazePathThread.clearCurrentStepData();
            return false;
        }
        mazeLog.regLogMaxStep("线程"+Thread.currentThread().getId()+"获取的下一个的", nextMaxtStep);
        /* 确定当前的下一点 */
        //mazeLog.regLogMazePoint("当前的7", mazePathThread.getCurrentPoint());
        mazeLog.regLogMazeDirection("线程"+Thread.currentThread().getId()+"获取的下一个的", nextDirection);
        nextPoint = getNextPoint(mazePathThread.getCurrentPoint(), nextDirection);
        mazeLog.regLogMazePoint("线程"+Thread.currentThread().getId()+"获取的下一个的", nextPoint);
        /* 算法数据更新 */
        setNextPointCanGo(mazePathThread.getCurrentPoint(), nextDirection);
        /* 线程刷新数据 */
        mazePathThread.goNewRouteNext(nextPoint, nextMaxtStep, nextDirection);
        mazeLog.regLogString("线程"+Thread.currentThread().getId()+"*******************  end  ********************\r\n");
        return true;
        //
    }
    /* 生成新的线程 */
    public synchronized void generateNewThreadHandle(MazePathThread mazeCurrentThread){
        if(!isGeneratNewThread()){
            return;
        }
        MazePathThread mazePathThread = new MazePathThread(mazeCurrentThread.getCurrentPoint());
        Thread thread = new Thread(mazePathThread);
        thread.start();
    }
    /* 判断当前是否还有有路可走的点 ,其中mazePoint是返回当前可走的节点*/
    public synchronized  boolean haveWay(MazePathThread mazePathThread) throws IOException {
        MazePoint point;
        while(!mazePathThread.stackThreadPoint.empty()){
            point = mazePathThread.stackThreadPoint.pop();
            if(haveWayToGo(point)){
                mazePathThread.setCurrentPoint(point);
                //如果发现当前结点可用，则再放进去
                mazePathThread.stackThreadPoint.push(point);
                mazeLog.regLogString("线程"+Thread.currentThread().getId()+"点（"+point.getMazeX()+","+point.getMazeY()+"）"+point.getSteped()+"可走");
                return true;
            }
            mazeLog.regLogString("线程"+Thread.currentThread().getId()+"点（"+point.getMazeX()+","+point.getMazeY()+"）"+point.getSteped()+"不可走");
        }
        mazeLog.regLogString("线程"+Thread.currentThread().getId()+"死了");
        return false;
    }
    /* 判断当前结点的周围是否还有路可以走 */
    public synchronized boolean haveWayToGo(MazePoint mazePoint){
        MazePoint point = new MazePoint();
        boolean result = false;
        if(isValieNextDirection(mazePoint, MazeDirection.UP, point)){
            currentThreadDirection.add(MazeDirection.UP);
            result |= true;
        }
        if(isValieNextDirection(mazePoint, MazeDirection.DOWN, point)){
            currentThreadDirection.add(MazeDirection.DOWN);
            result |= true;
        }
        if(isValieNextDirection(mazePoint, MazeDirection.LEFT, point)){
            currentThreadDirection.add(MazeDirection.LEFT);
            result |= true;
        }
        if(isValieNextDirection(mazePoint, MazeDirection.RIGHT, point)){
            currentThreadDirection.add(MazeDirection.RIGHT);
            result |= true;
        }

        return result;
    }
    /* 是否生成新的线程 */
    public boolean isGeneratNewThread(){
        Random random = new Random();
        double rate = random.nextInt(10);
        if(rate < newThreadGeneratRate*10){
            return true;
        }
        return false;
    }
    /* 获取下一个节点 */
    public synchronized MazePoint getNextPoint(MazePoint currentPoint, MazeDirection mazeNextDirection){
        MazePoint threadNextPoint = new MazePoint();
        if(isValieNextDirection(currentPoint, mazeNextDirection, threadNextPoint)){
            return getMazePoint(threadNextPoint);
        }
        return null;
    }
    /* 判断下一步的方向是否是可走的 ,这个判断是否下一个点已经被人走了，或者撞墙*/
    public synchronized boolean isValieNextDirection(MazePoint mazePoint, MazeDirection mazeDirection, MazePoint threadNextPoint){
        boolean valid = false;
        switch(mazeDirection){
            case UP:
                if(mazePoint.getMazeY() - 1 >= 0){
                    mazePoints[mazePoint.getMazeX()][mazePoint.getMazeY() - 1].clone(threadNextPoint);
                    valid = threadNextPoint.isPointCanGo();
                }
                break;
            case DOWN:
                if(mazePoint.getMazeY() + 1 < mazeHight){
                    mazePoints[mazePoint.getMazeX()][mazePoint.getMazeY() + 1].clone(threadNextPoint);
                    valid = threadNextPoint.isPointCanGo();
                }
                break;
            case LEFT:
                if(mazePoint.getMazeX() - 1 >= 0){
                    mazePoints[mazePoint.getMazeX() - 1][mazePoint.getMazeY()].clone(threadNextPoint);
                    valid = threadNextPoint.isPointCanGo();
                }
                break;
            case RIGHT:
                if(mazePoint.getMazeX() + 1 < mazeWidth){
                    mazePoints[mazePoint.getMazeX() + 1][mazePoint.getMazeY()].clone(threadNextPoint);
                    valid = threadNextPoint.isPointCanGo();
                }
                break;
            default:break;
        }
        return valid;
    }
    /* 获取下一步要走的方向 */
    public synchronized MazeDirection getNextDirection(){
        MazeDirection mazeDirection;
        Random random = new Random();
        MazePoint mazePoint = new MazePoint();
        int directionIdx;
        int directionNum = currentThreadDirection.size();
        if(directionNum <= 0){
            return MazeDirection.BUTT;
        }
        directionIdx = random.nextInt(directionNum);
        mazeDirection = currentThreadDirection.get(directionIdx);
        currentThreadDirection.remove(directionIdx);
        return mazeDirection;
    }
    /* 获取下一步要走的最大步数 */
    public synchronized int getNextMaxStep(MazePathThread mazePathThread, MazeDirection mazeDirection){
        Random random = new Random();
        int stepIdx;
        MazePoint nextOtherPoint = mazePathThread.getCurrentPoint();
        for(stepIdx = 0; stepIdx < routeMaxStep; stepIdx++){
            nextOtherPoint = getNextPoint(nextOtherPoint, mazeDirection);
            if(nextOtherPoint == null){
                break;
            }
        }
        if(stepIdx <= 0){
            return 0;
        }
        return random.nextInt(stepIdx)+1;
    }
    class MazePathThread implements Runnable {
        private MazePoint currentPoint;
        public Stack<MazePoint> stackThreadPoint;        //表示每个线程走过的节点
        private int currentMaxStepNum;                    //当前这一步要走的最大步数
        private int currentStepCount;                    //当前的已经走了多少步
        private MazeDirection currentDirection;         //当前的方向

        public void run() {
            try {
                while(haveWay(this)) {
                    if(mazeStartFlg == false){
                        return;
                    }
                    /* 走下一步 */
                    try {
                        if (!goNext(this)) {
                            continue;
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    /* 刷新图形 */
                    maze.refreshFace();
                    try {
                        Thread.sleep(getRouteRate());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                mazeLog.regLogString("线程"+Thread.currentThread().getId()+"\r\n");
                mazeLog.regLogPrintMazeMap(mazePoints, mazeWidth, mazeHight, mazeStartPoint);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if(mazeNoteNum >= mazeWidth*mazeHight){
                maze.mazeStop();
            }
        }

        public void clear() {
            currentPoint.clear();
            stackThreadPoint.clear();
            currentDirection = MazeDirection.BUTT;
        }

        public MazePathThread(MazePoint currentPoint) {
            currentMaxStepNum = 0;
            this.currentPoint = currentPoint;
            currentStepCount = 0;
            stackThreadPoint = new Stack<MazePoint>();
            stackThreadPoint.push(currentPoint);
        }

        public synchronized int getCurrentStepCount() {
            return this.currentStepCount;
        }

        public int getCurrentMaxStepNum() {
            return this.currentMaxStepNum;
        }

        public MazeDirection getCurrentDirection() {
            return this.currentDirection;
        }
        /* 清理当前这一步的数据 */
        public void clearCurrentStepData(){
            currentStepCount = 0;
        }

        public MazePoint getCurrentPoint() {
            return this.currentPoint;
        }

        public synchronized void setCurrentPoint(MazePoint mazePoint) {
            this.currentPoint = mazePoint;
        }

        public void setCurrentDirection(MazeDirection mazeDirection) {
            this.currentDirection = mazeDirection;
        }

        public void setCurrentStepCount(int currentStepCount) {
            this.currentStepCount = currentStepCount;
        }

        public void setCurrentMaxStepNum(int currentMaxStepNum) {
            this.currentMaxStepNum = currentMaxStepNum;
        }

        /*生成新的路径*/
        public void generatNewPath() {
//            Direction direction = getDirection(x, y);
            //goNext(x,y,true);
        }

        /*不改变方向走下一步*/
        public synchronized void goOldRouteNext(MazePoint mazePoint) {
            currentStepCount++;
            currentPoint = mazePoint;
            stackThreadPoint.push(mazePoint);
        }

        /*改变方向走下一步*/
        public synchronized void goNewRouteNext(MazePoint mazePoint, int nextMaxStepNum, MazeDirection nextDirection) {
            currentStepCount = 1;
            currentPoint = mazePoint;
            stackThreadPoint.push(mazePoint);
            this.currentMaxStepNum = nextMaxStepNum;
            this.currentDirection = nextDirection;
        }
    }
}

class MazeLog{
    public boolean logSwitch = false;
    String logPath;
    String logName;
    File filedir;
    File file;
    public MazeLog(String filePath, String fileName) throws IOException {
        this.logPath = filePath;
        this.logName = fileName;
        filedir = new File(logPath);
        file = new File(logPath,logName);
        filedir.mkdirs();
        file.createNewFile();
    }
    public synchronized void regLog(String str) throws IOException {
        if(logSwitch == true) {
            if (!filedir.exists()) {
                filedir.mkdirs();
            }
            if (!file.exists()) {
                file.createNewFile();
            }
            FileOutputStream fileOutputStream = new FileOutputStream(file, true);
            fileOutputStream.write(str.getBytes());
            fileOutputStream.close();
        }
    }
    public synchronized void regLogString(String str) throws IOException {
        regLog(str+"\r\n");
    }
    public synchronized void regLogStringAddTime(String str){
        Date now = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");//可以方便地修改日期格式
        try {
            regLog(dateFormat.format(now)+ "     "+str+"\r\n");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public synchronized void regLogMazePoint(String str, MazePoint mazePoint) {
        Date now = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");//可以方便地修改日期格式
        try {
            regLog(dateFormat.format(now)
                    + "     "
                    + str+"点:"+"("+mazePoint.getMazeX()
                    + ","+mazePoint.getMazeY()+")"
                    +"当前状态："+"up("+mazePoint.getUpSteped()+") left("+mazePoint.getLeftSteped()+") face("+mazePoint.getSteped()+")\r\n");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public synchronized void regLogMazeDirection(String str, MazeDirection nextDirection) throws IOException {
        Date now = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");//可以方便地修改日期格式
        regLog(dateFormat.format(now) +"     "+str+"方向 "+nextDirection.toString()+"\r\n");
    }
    public synchronized void regLogMaxStep(String str, int maxStepNum) throws IOException {
        Date now = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");//可以方便地修改日期格式
        regLog(dateFormat.format(now) +"     "+str+"最大步长是 "+maxStepNum+"\r\n");
    }
    public synchronized void regLogPrintMazeMap(MazePoint[][] mazePoints,
                                   int width, int height,
                                   MazePoint startPoint) throws IOException {
        String strFlag = "O";
        for(int i = 0; i< height;i++){
            regLog("          ");
            for (int j = 0; j < width; j++) {
                if (mazePoints[j][i].getSteped()) {
                    strFlag = "*";
                } else {
                    strFlag = "O";
                }
                if(startPoint.equalsPoint(mazePoints[j][i])){
                    strFlag = "#";
                }
                regLog("|" + strFlag);
            }
            regLog("|\r\n");
        }
        regLog("\r\n");
    }
}